using System;
using System.Diagnostics;
using UnityEngine;

namespace DanMuHelper.Tools
{
    // TODO: 单例分类：脚本自动创建，场景中挂载
    /// <summary>
    ///   MonoBehaviour单例模式
    /// </summary>
    public class MonoBehaviourSingleton<T> : MonoBehaviour where T : MonoBehaviour
    {
        /// <summary>
        /// 游戏正在退出
        /// </summary>
        protected static bool _shuttingDown = false;
        private static T _instance;

        /// <summary>
        /// 检测到单例重复时，故意销毁
        /// </summary>
        protected bool _destroyOnPurpose;
        protected bool _awaken;

        // private static string TAG
        // {
        //     get => typeof(T).FullName;
        // }

        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    if (_shuttingDown)
                    {
                        // 游戏即将退出, 没有必要再创建新的实例
                        LogInfo($"游戏即将退出, 不会再实例化新的单例 ({typeof(T).FullName}), 请检查是否在OnDestroy, OnDisable方法里调用了单例接口.");
                        return null;
                    }

#if UNITY_EDITOR
                    var targets = GameObject.FindObjectsOfType<T>();
                    if (null == targets || targets.Length == 0)
                    {
                        var singletonObject = new GameObject("[" + typeof(T).Name + "]", typeof(T));
                        LogInfo($"Singleton instantiated via script ! [{singletonObject.name}]");
                    }
                    else
                    {
                        // Editor模式下，检测单利重复
                        _instance = targets[0];
                        if (targets.Length > 1)
                        {
                            LogError($"存在多个单例！！[{typeof(T)}]");
                        }
                    }
#else
                    _instance = FindObjectOfType<T>();
                    if (null == _instance)
                    {
                        var singletonObject = new GameObject("[" + typeof(T).Name + "]", typeof(T));
                        LogInfo($"Singleton instantiated via script ! [{singletonObject.name}]");
                    }
#endif
                }
                return _instance;
            }
        }

        /// <summary>
        /// 替代Awake
        /// </summary>
        protected virtual void SingletonAwake()
        {
        }

        /// <summary>
        /// 替代OnDestroy
        /// </summary>
        protected virtual void SingletonDestroy()
        {
        }

        /// <summary>
        /// 封死Awake接口, 使用OnInit替代
        /// </summary>
        protected void Awake()
        {
            if (_instance == null)
            {
                _instance = this as T;
                _awaken = true;
                DontDestroyOnLoad(gameObject);
                SingletonAwake();
            }
            else
            {
                LogError($"单例`{typeof(T).FullName}`重复创建! (可能在场景中已经有实例, 但是场景加载前调用了Instance接口, 或者场景中手动创建了多个实例)");
                // 故意销毁
                _destroyOnPurpose = true;
                GameObject.Destroy(gameObject);
            }
        }
        protected void Start()
        {
            if (!_awaken)
            {
                LogError("子类型屏蔽了Awake方法!");
            }
        }

        protected virtual void OnApplicationQuit()
        {
            _shuttingDown = true;
        }

        /// <summary>
        /// 当播放停止时，Unity 会以随机顺序销毁对象
        /// 若单例 gameObject 先于其他对象销毁，不排除这个单例再次被调用的可能性。
        /// 故而在编辑器模式下，即便播放停止了，也可能会生成一个 gameObject 对象残留在编辑器场景中。
        /// 所以，此方法中加把锁，避免不必要的单例调用
        /// </summary>
        protected void OnDestroy()
        {
            if (_destroyOnPurpose)
            {
                return;
            }
            if (_instance != null && _instance == this)
            {
                SingletonDestroy();
                _destroyOnPurpose = true;
                _instance = null;
            }
        }
        #region Logging
        [Conditional("UNITY_EDITOR")]
        private static void LogInfo(string message)
        {
            UnityEngine.Debug.Log($"[{nameof(MonoBehaviourSingleton<T>)}] {message}");
        }
        private static void LogError(string message)
        {
            UnityEngine.Debug.LogError($"[{nameof(MonoBehaviourSingleton<T>)}] {message}");
        }
        #endregion
    }
}
